#pragma once

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "../comm/log.hpp"
#include "../comm/utils.hpp"

namespace ns_runner
{
    using namespace ns_log;
    using namespace ns_utils;
    using std::endl;

    class Runner
    {
    private:
        /**
         * @brief 限制进程使用cpu、memory资源大小
         *
         * @param[in] cpu_limit  使用cpu最大时间，单位为秒
         *
         * @param[in] mem_limit  使用memory最大大小，单位为KB
         *
         */
        static void set_process_limit(int cpu_limit,int mem_limit)
        {
            // 限制cpu
            struct rlimit cpu;
            cpu.rlim_max = RLIM_INFINITY;
            cpu.rlim_cur = cpu_limit;
            setrlimit(RLIMIT_CPU,&cpu);

            // 限制内存
            struct rlimit mem;
            mem.rlim_max = RLIM_INFINITY;
            mem.rlim_cur = mem_limit * 1024; // byte 转换为KB
            setrlimit(RLIMIT_AS,&mem);
        }
    public:
        /**
         * @brief 运行可执行程序
         *
         * @param[in] file 待运行可执行程序文件名，不含后缀名
         *
         * @param[in] cpu_limit 使用cpu最大时间，单位为秒
         *
         * @param[in] mem_limit  使用memory最大大小，单位为KB
         *
         * @return 状态码
         *         > 0 程序运行异常收到信号，状态码为对应信号
         *        == 0 程序运行成功，但不能确定运行结果是否正确
         *         < 0 内部错误
         *
         */
        static int run(const string& file,int cpu_limit,int mem_limit)
        {
            string exec_file = PathUtil::exec_file(file);
            string _stdin_file = PathUtil::stdin_file(file);
            string _stdout_file = PathUtil::stdout_file(file);
            string _stderr_file = PathUtil::stderr_file(file);

            umask(0);
            int new_stdin = open(_stdin_file.c_str(),O_CREAT | O_WRONLY,0644);
            int new_stdout = open(_stdout_file.c_str(),O_CREAT | O_WRONLY,0644);
            int new_stderr = open(_stderr_file.c_str(),O_CREAT | O_WRONLY,0644);
            if(-1 == new_stdin || -1 == new_stdout || -1 == new_stderr)
            {
                LOG(ERROR) << "创建运行临时文件失败，无法进行下一步运行任务" << endl;

                return -2;
            }

            pid_t pid = fork();
            if(-1 == pid)
            {
                close(new_stdin);
                close(new_stdout);
                close(new_stderr);
                LOG(ERROR) << "创建子进程失败，无法进行下一步运行任务" << endl;

                return -1;
            }
            else if(0 == pid) // 子进程
            {
                dup2(new_stdin,0);
                dup2(new_stdout,1);
                dup2(new_stderr,2);
                /* 对运行任务进行资源限制 */
                set_process_limit(cpu_limit,mem_limit);
                execl(exec_file.c_str(),exec_file.c_str(), nullptr);

                LOG(ERROR) << "程序替换失败,无法进行下一步运行任务" << endl;
                exit(1);
            }
            else
            {
                close(new_stdin);
                close(new_stdout);
                close(new_stderr);

                int status = 0;
                waitpid(pid,&status,0);
                LOG(INFO) << "程序运行完毕,信号值：" << (status & 0x7F) << endl;

                return status & 0x7F;
            }
        }
    };
}
